package com.wulis.config.security;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.wulis.common.annotation.CrossAuth;
import com.wulis.config.swagger.SwaggerSecurityConfig;

/**
 * @author WuliBao
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    
    @Resource
    private SwaggerSecurityConfig swaggerSecurityConfig;
    
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;
    
    @Resource
    private AuthenticationTokenFilter authenticationTokenFilter;
    
    @Resource
    private RequestMappingHandlerMapping requestMappingHandlerMapping;
    
    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    /**
     * 请求拦截设置
     *
     * @param httpSecurity httpSecurity
     * @throws Exception Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity)
        throws Exception {
        // 拥有swagger时放行策略
        swaggerSecurityConfig.configure(httpSecurity);
        // 统一放行策略
        httpSecurity
            // 禁用csrf，因为不使用session
            .csrf()
            .disable()
            // 基于token，所以不需要session
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            // 认证失败处理类
            .exceptionHandling()
            // 认证用户访问无权限资源时异常
            .accessDeniedHandler(accessDeniedHandler)
            // 匿名用户访问无权限资源时的异常
            .authenticationEntryPoint(authenticationEntryPoint)
            .and()
            // 过滤请求
            .authorizeRequests()
            // 放行所有OPTIONS预检请求
            .antMatchers(HttpMethod.OPTIONS, "/**")
            .permitAll()
            // 放行资源
            .antMatchers(getAntMatchers())
            .permitAll()
            // 拦截其他请求
            .anyRequest()
            .authenticated()
            .and()
            // JWT Filter
            .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    /**
     * 通过注解方式实现security资源放行
     *
     * @return antMatchers
     */
    private String[] getAntMatchers() {
        Set<String> set = new HashSet<>();
        // 获取项目所有controller方法及路径
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            // 判断是否包含CrossAuth注解
            CrossAuth annotation = handlerMethod.getMethod().getAnnotation(CrossAuth.class);
            if (annotation != null) {
                RequestMappingInfo requestMappingInfo = entry.getKey();
                PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
                set.addAll(patternsCondition.getPatterns());
            }
        }
        return set.toArray(new String[0]);
    }
}
